package cn.enilu.flash.service.api;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.enilu.flash.bean.constant.CfgKey;
import cn.enilu.flash.bean.entity.shop.ShopUser;
import cn.enilu.flash.bean.entity.system.Cfg;
import cn.enilu.flash.bean.exception.ApplicationException;
import cn.enilu.flash.bean.exception.ApplicationExceptionEnum;
import cn.enilu.flash.bean.vo.JwtUser;
import cn.enilu.flash.bean.vo.UserInfo;
import cn.enilu.flash.bean.vo.shop.WechatInfo;
import cn.enilu.flash.bean.vo.shop.WechatLoginResult;
import cn.enilu.flash.bean.vo.shop.WechatUserInfoParam;
import cn.enilu.flash.cache.CacheDao;
import cn.enilu.flash.security.JwtUtil;
import cn.enilu.flash.security.UserService;
import cn.enilu.flash.service.shop.ShopUserService;
import cn.enilu.flash.service.system.CfgService;
import cn.enilu.flash.utils.HttpUtil;
import cn.enilu.flash.utils.StringUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.exceptions.StatefulException;
import me.chanjar.weixin.common.error.WxErrorException;
import org.nutz.http.Http;
import org.nutz.json.Json;
import org.nutz.mapl.Mapl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;

/**
 * @author ：enilu
 * @date ：Created in 1/19/2020 4:31 PM
 */
@Service
public class WeixinService {
    @Autowired
    private CfgService cfgService;
    @Autowired
    private ShopUserService shopUserService;
    @Autowired
    private CacheDao cacheDao;
    @Autowired
    private WxMaService wxMaService;
    @Autowired
    private UserService userService;

    private Logger logger = LoggerFactory.getLogger(WeixinService.class);

    public void updateWeixinToken() throws WxErrorException {
        HttpUtil.trustEveryone();
        String accessToken = getAccessToken();
        String jsapiTicket = getJsapiTicket(accessToken);

        Cfg tokenCfg = cfgService.getByCfgName(CfgKey.WX_ACCESS_TOKEN);
        if (tokenCfg == null) {
            tokenCfg = new Cfg();
            tokenCfg.setCfgName(CfgKey.WX_ACCESS_TOKEN);
            tokenCfg.setCfgDesc("微信token，通过定时任务获取");
        }
        tokenCfg.setCfgValue(accessToken);
        cfgService.saveOrUpdate(tokenCfg);

        Cfg tidCfg = cfgService.getByCfgName(CfgKey.WX_JS_API_TICKET);
        if (tidCfg == null) {
            tidCfg = new Cfg();
            tidCfg.setCfgName(CfgKey.WX_JS_API_TICKET);
            tidCfg.setCfgDesc("微信ticket，通过定时任务获取");
        }
        tidCfg.setCfgValue(jsapiTicket);
        cfgService.saveOrUpdate(tidCfg);
    }

    public String getAccessToken() throws WxErrorException {
        return wxMaService.getAccessToken();
    }

    public String getJsapiTicket(String accessToken) throws WxErrorException {
        return wxMaService.getJsapiService().getJsapiTicket();
    }


    public boolean isAuth(ShopUser user, String code) throws WxErrorException {
        WechatLoginResult wechatInfo = null;
        if (user != null) {
            wechatInfo = cacheDao.hget(CacheDao.SESSION, "WECHAT_INFO" + user.getId(), WechatLoginResult.class);
        }
        if (wechatInfo != null) {
            logger.info("从缓存获取微信用户信息", Json.toJson(wechatInfo));
            return true;
        }
        if (StringUtil.isNotEmpty(code)) {
            wechatInfo = getWechatInfoByCode(code);
            if (wechatInfo != null) {
                WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
                ShopUser old = shopUserService.findByWechatOpenId(sessionInfo.getOpenid());
                if (old != null && old.getId().intValue() != user.getId().intValue()) {
                    throw new ApplicationException(ApplicationExceptionEnum.WECHAT_BIND_ANOTHER);
                }
                //user.setWechatNickName(StringUtil.getValidChar(wechatInfo.getNickName()));
                //user.setWechatHeadImgUrl(wechatInfo.getHeadUrl());
                //user.setWechatOpenId(wechatInfo.getOpenId());
                if (StringUtil.equals(user.getNickName(), user.getMobile())) {
                    user.setNickName(user.getWechatNickName());
                }
                shopUserService.update(user);
                cacheDao.hset(CacheDao.SESSION, "WECHAT_INFO" + user.getId(), wechatInfo);
                return true;
            }
        }

        return false;
    }

    public WechatLoginResult getWechatInfoByCode(String code) throws WxErrorException {
        WxMaJscode2SessionResult jscode2SessionResult = wxMaService.getUserService().getSessionInfo(code);
        ShopUser shopUser = shopUserService.findByWechatOpenId(jscode2SessionResult.getOpenid());
        if (shopUser == null) {
            WechatInfo wechatInfo = new WechatInfo();
            wechatInfo.setOpenId(jscode2SessionResult.getOpenid());
            shopUser = shopUserService.registerByWechatInfo(wechatInfo);
        }
        //生成token
        String token = userService.loginForToken(new JwtUser(shopUser));
        WechatLoginResult wechatLoginResult = new WechatLoginResult();
        wechatLoginResult.setToken(token);
        wechatLoginResult.setSessionKey(jscode2SessionResult.getSessionKey());
        return wechatLoginResult;
    }

    public WechatInfo getWechatInfo(String openId) {

        String accessToken = cfgService.getCfgValue(CfgKey.WX_ACCESS_TOKEN);
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=" + accessToken + "&openid=" + openId;
        String result = Http.get(url).getContent();

        Object object = Json.fromJson(StringUtil.sNull(result));

        logger.info("getWecchatInfo====url:" + url + ";  response:" + Json.toJson(object));
        if (Mapl.cell(object, "errcode") != null) {
            logger.info("获取微信用户基本信息失败", Mapl.cell(object, "errmsg"));
        } else {
            if (StringUtil.equals(StringUtil.sNull(Mapl.cell(object, "errcode")), "0")) {
                logger.error("用户:{}没有关注该公众号", openId);
            } else {
                WechatInfo wechatInfo = new WechatInfo();
                wechatInfo.setOpenId(openId);
                //wechatInfo.setNickName(StringUtil.sNull(Mapl.cell(object, "nickname")));
                //wechatInfo.setHeadUrl(StringUtil.sNull(Mapl.cell(object, "headimgurl")));
                return wechatInfo;
            }
        }
        return null;
    }

    private String createNonceStr() {
        return UUID.randomUUID().toString();
    }

    private String createTimestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }

    private String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    public Map<String, String> getSign(String url) {
        Map<String, String> map = getSign(cfgService.getCfgValue(CfgKey.WX_JS_API_TICKET), url);
        map.put("appId", cfgService.getCfgValue(CfgKey.WX_APP_ID));
        return map;
    }

    public Map<String, String> getSign(String jsapi_ticket, String url) {
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = createNonceStr();
        String timestamp = createTimestamp();
        String string1;
        String signature = "";

        // 注意这里参数名必须全部小写，且必须有序
        string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str + "&timestamp=" + timestamp + "&url=" + url;
        logger.info(string1);
        try {
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            signature = byteToHex(crypt.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);

        return ret;
    }

    public UserInfo perfectUserInfo(WechatUserInfoParam wechatUserInfo, Long userId) {
        ShopUser shopUser = shopUserService.get(userId);
        if (shopUser == null) {
            throw new ApplicationException(ApplicationExceptionEnum.USER_NOT_EXISTS);
        }
        if (StringUtil.isEmpty(shopUser.getWechatNickName())) {
            WxMaUserInfo userInfo = wxMaService.getUserService().getUserInfo(wechatUserInfo.getSessionKey(), wechatUserInfo.getEncryptedData(), wechatUserInfo.getIv());
            shopUser.setWechatNickName(userInfo.getNickName());
            shopUser.setWechatHeadImgUrl(userInfo.getAvatarUrl());
            shopUser.setAvatar(userInfo.getAvatarUrl());
            shopUser.setGender(userInfo.getGender());
            shopUser.setNickName(userInfo.getNickName());
            shopUserService.update(shopUser);
        } else {
            shopUser.setLastLoginTime(new Date());
            shopUserService.update(shopUser);
        }
        UserInfo userInfo=new UserInfo();
        userInfo.setRefreshWechatInfo(false);
        BeanUtil.copyProperties(shopUser, userInfo,true);
        return userInfo;
    }
}
